import logging
import json
import random
import string

from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection   #redis连接模块
from django.http import HttpResponse #JsonResponse 已在utils.json_fun中重写

from utils.captcha.captcha import captcha   #生成图片验证码的小工具
from utils.res_code import Code,error_map #自定义错误码
from . import constants     #各种过期时间设置
from user.models import Users #导入用户表
from utils.json_fun import to_json_data #重新封装JsonResponse
from . import forms #校验表单
from utils.yuntongxun.sms import CCP

from celery_tasks.sms.tasks import send_sms_code
# 导入日志器
logger = logging.getLogger('django') #获取日志记录者

# 1.定义一个图形验证码视图
class ImageCode(View):
    '''
    define image verification view
    定义一个图形验证码视图
    请求方法： GET
    url定义: /image_codes/<uuid:image_code_id>/
    测试用url: /image_codes/6e03466b-17b4-4234-9aa7-449cd77ebf9c/
    '''
    # 2.获取前端参数 uuid 并且校验
    def get(self,request,image_code_id):
    # 3.生成验证码文本和验证码图片
        text,image = captcha.generate_captcha()
    # 4.建立redis连接
        con_redis = get_redis_connection(alias='verify_codes')#指定连接redis数据库
    # 5.保存验证码文本
        img_key = "img_{}".format(image_code_id).encode('utf-8')   #生成一个key
        # 将图片验证码的key和验证码文本保存到redis中，并设置过期时间
        con_redis.setex(img_key, constants.IMAGE_CODE_REDIS_EXPIRES, text)
        logger.info("Image code: {}".format(text))  #以info等级发给日志记录器触发(记录,console)
    # 6.将验证码图片反回前端
        return HttpResponse(content=image,content_type='images/jpg')#申明内容类型为 文件/图片

# 1.定义一个用户名校验视图
class CheckUsernameView(View):
    '''
    check whether the user exists
    用户名校验视图
    url定义:usernames/(?P<username>\w{5,20})/
    url测试:/user/usernames/hlong/
    '''
    # 2.获取前端参数 username 并且校验
    def get(self,requset,username):
    # 3.查询数据
        count = Users.objects.filter(username=username).count()#查询数量
    # 4.编辑校验结果
        data = {
            'username' : username, #所查询的用户
            'count':count,      #True if count else False,布尔值 0没注册 非0注册了
        }
    # 5.反回校验结果前端
    #     return JsonResponse(data=data)#以json格式反回前端
        return to_json_data(data=data)#以json格式反回前端

# 1.定义一个手机号校验视图
class CheckMobileView(View):
    '''
    check whether the mobile exists
    用户名校验视图
    url定义:mobiles/(?P<mobile>1[3-9]\d{9}/
    url测试:/user/mobiles/13888888888
    '''
    # 2.获取前端参数 mobile 并且校验
    def get(self,requset,mobile):
    # 3.查询数据
        count = Users.objects.filter(mobile=mobile).count()#查询数量
    # 4.编辑校验结果
        data = {
            'mobile' : mobile, #所查询的用户
            'count':count,      #True if count else False,布尔值 0没注册 非0注册了
        }
    # 5.反回校验结果前端
    #     return JsonResponse(data=data)#以json格式反回前端
        return to_json_data(data=data)#以json格式反回前端

# 1.定义一个发送短信验证码视图
class SmsCodesView(View):
    '''
     send mobile sms code
     发送短信验证码
     此视图主要返回报错信息
    '''
    # 2.获取前端参数 mobile uuid
    def post(self,request):
        json_data = request.body    #获取请求体
        if not json_data: #BUG  前端为空则报错
            return to_json_data(errno=Code.PARAMERR,errmsg="前端参数为空,请重新输入")
        dict_data = json.loads(json_data.decode('utf8')) #json转成字典
    # 3.校验参数
        '''
        mobile = dict_data.get('mobile')
        text = dict_data.get('text')
        image_code = dict_data.get('image_code')

        由于需要校验以下六项内容 适合使用form表单校验  ???序列化器???
        手机号是否 空
        手机号是否 11位
        手机号是否 存在
        图形验证码文是否 空
        图形验证码是否 uuid存在(过期删除)
        图形验证码是否 文本匹配
        '''
        form = forms.CheckImgCodeForm(data=dict_data)
        if form.is_valid():#执行清洗函数成功True,失败Fals
            # 4.发送短信验证码
            # 获取手机号
            mobile = form.cleaned_data.get('mobile')#所有清洗过的数据都放在 cleaned_data 中
            # 生成验证码
            sms_num = ''.join([random.choice(string.digits) for _ in range(constants.SMS_CODE_NUMS)]).encode('utf-8')#digits = '0123456789'
            # 5.保存短信验证码
            redis_con = get_redis_connection(alias='verify_codes')  # redis连接器
            sms_flag_fmt = "sms_flag_{}".format(mobile).encode('utf-8') #短信发送状态
            sms_text_fmt = "sms_{}".format(mobile).encode('utf-8') #短信文本
            p1 = redis_con.pipeline()#定义一个redis管道,用于统一输入
            try:
                p1.setex(sms_flag_fmt,constants.SEND_SMS_CODE_INTERVAL,1)#发送间隔
                p1.setex(sms_text_fmt,constants.SMS_CODE_REDIS_EXPIRES,sms_num)#短信验证码有效期，单位分钟
                p1.execute()#提交redis
            except Exception as e:
                logger.debug('redis执行一次,{}'.format(e))
                send_sms_code()
                return to_json_data(errno=Code.UNKOWNERR,errmsg=error_map[Code.UNKOWNERR])
            # 6.发送短信验证码(云通信接口:已欠费)
            logger.info("发送验证码短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))
            return to_json_data(errno=Code.OK, errmsg="短信验证码发送成功")
            # try:
            #     result = CCP().send_template_sms(
            #          mobile,#指定用户手机号
            #          [sms_num, constants.SMS_CODE_REDIS_EXPIRES],#[短信验证码,短信验证码有效期5分钟]
            #          constants.SMS_CODE_TEMP_ID)# 短信发送模板
            # except Exception as e:
            #     logger.error("发送验证码短信[异常][ mobile: %s, message: %s ]" % (mobile, e))#记录异常
            #     return to_json_data(errno=Code.SMSERROR, errmsg=error_map[Code.SMSERROR])#发送短信验证码异常
            # else:
            #     if result == 0:
            #         logger.info("发送验证码短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))
            #         return to_json_data(errno=Code.OK, errmsg="短信验证码发送成功")
            #     else:
            #         logger.warning("发送验证码短信[失败][ mobile: %s ]" % mobile)
            #         return to_json_data(errno=Code.SMSFAIL, errmsg=error_map[Code.SMSFAIL])
        # 表单校验失败
        else:
        # 定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():#获取form表单错误信息
                err_msg_list.append(item[0].get('message'))
                # print(item[0].get('message'))   # for test
            err_msg_str = '/'.join(err_msg_list)  # 拼接错误信息为一个字符串

            return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)#参数错误 错误信息

    # 7.反回前端 return to_json_data()
